
#include "Thread.h"

#if 1 // BASTERD_IS_POSIX

namespace basterd
{
	// --------------------------------------------------------------------
	// Thread interface
	// --------------------------------------------------------------------
	uint32 Thread::PRIORITY_MAX = 100;
	// --------------------------------------------------------------------
	uint32 Thread::PRIORITY_MIN = 0;
	// --------------------------------------------------------------------
	uint32 Thread::PRIORITY_NORMAL = 50;
	// --------------------------------------------------------------------
	Thread::Thread(void)
		: _runnable(0), _thread(0L),
		  _priority(Thread::PRIORITY_NORMAL),
		  _state(Thread::THREAD_IDLE)
	{

	}
	// --------------------------------------------------------------------
	Thread::Thread( Runnable* runner )
		: _runnable(runner), _thread(0L),
		  _priority(Thread::PRIORITY_NORMAL),
		  _state(Thread::THREAD_IDLE)
	{

	}
	// --------------------------------------------------------------------
	Thread::~Thread(void)
	{
		// deletion
		if( (_state != Thread::THREAD_IDLE) && (_state != Thread::THREAD_STOPPED) )
		{
			//BASTERD_WARNF("Thread <font color=orange>" << _thread << "</font>is still running on destructor");
			stop ();
		}
	}
	// --------------------------------------------------------------------
	void Thread::setPriority( uint32 pr )
	{
		_priority = pr;
	}
	// --------------------------------------------------------------------
	uint32 Thread::getPriority(void) const
	{
		return _priority;
	}
	// --------------------------------------------------------------------
	void Thread::start(void)
	{
		if( (_state == Thread::THREAD_READY) )
		{
			// must wait until this thread stops
			//BASTERD_WARN("Thread is ready to run");
			return;
		}

		if( (_state == Thread::THREAD_RUNNING) )
		{
			// must wait until this thread stops
			//BASTERD_WARN("Thread is already running");
			return;
		}

		pthread_attr_t attribs;
		int rc;

		// NOTE: posix threads are joinable by default
		pthread_attr_init(&attribs);
		// pthread_attr_setdetachstate(&attribs, PTHREAD_CREATE_JOINABLE );
		rc = pthread_create(&_thread, &attribs, _threadRunMethod, (void*)this );

		if( rc )
		{
			std::string err = "unknown error";
			switch( rc )
			{
			case 11: // EAGAIN
				//BASTERD_ERRF("Thread <font color=red>" <<  _thread << "</font> cannot be created: Out of system resources.");
				break;
			case 22: // EINVAL
				//BASTERD_ERRF("Thread <font color=red>" <<  _thread << "</font> cannot be created: Wrong thread attributes.");
				break;
			case 1: // EPERM
				//BASTERD_ERRF("Thread <font color=red>" <<  _thread << "</font> cannot be created: No permission.");
				break;
			default:
				//BASTERD_ERRF("Thread <font_state = Thread::THREAD_READY; color=red>" <<  _thread << "</font> cannot be created: Unknown error ");
				break;
			}

			_state = Thread::THREAD_IDLE;
		}else
		{
			_state = Thread::THREAD_READY;
			///BASTERD_LOGF("Thread <font color=blue>" << _thread << "</font> created");


			// sets priority:
			struct sched_param sched;
			memset(&sched, 0, sizeof(struct sched_param));
			//sched.sched_priority = priority;

			sched.sched_priority = sched_get_priority_max(SCHED_OTHER);
			if ( pthread_setschedparam( _thread, SCHED_OTHER, &sched) != 0 )
			{
				//BASTERD_WARNF("Could not set priority of thread <font color=orange>" << _thread << "</font>");
			}
		}

		//

	}
	// --------------------------------------------------------------------
	void Thread::stop(void)
	{
		//MutexLocker ml(_mutex);

		//BASTERD_LOGF("trying to stop thread...");

		if( (_state==Thread::THREAD_READY) || (_state==Thread::THREAD_RUNNING) )
		{
			#if BASTERD_IS_WINDOWS
				if( _thread )
				{
					bool stillRunning = !TerminateThread (_thread, ~0);
					_state = Thread::THREAD_READY;
				}
			#else
				// pthread version
				int error = pthread_cancel (_thread);
				if( !error )
				{
					_state = Thread::THREAD_STOPPED;
				}else
				{
					// ERROR: thread still running
					// TODO log thread cancel error
				}
			#endif
		}
	}
	// --------------------------------------------------------------------
	void Thread::sleep( uint32 milliseconds )
	{
		#if BASTERD_IS_WINDOWS
			Sleep(milliseconds);
		#else

			uint32 microseconds = milliseconds*1000;
			#if 1 /*defined(HAVE_NANOSLEEP)*/
				timespec tmReq;
				tmReq.tv_sec = (time_t)(microseconds / 1000000);
				tmReq.tv_nsec = (microseconds % 1000000) * 1000;

				// we're not interested in remaining time nor in return value
				(void)nanosleep(&tmReq, (timespec *)NULL);
			#elif defined(HAVE_USLEEP)
				usleep(microseconds);

			#elif defined(HAVE_SLEEP)
				// sleep does it in seconds
				::sleep(microseconds * 1000000);
			#else
				::sleep( microseconds );

			#endif
		#endif
	}
	// --------------------------------------------------------------------
	void Thread::join(void)
	{
#if BASTERD_THREAD_INTERFACE
		// cannot join a thread that is not running...
		if( _state == Thread::THREAD_READY )
		{
			// waits until this thread is running
			while( !isRunning() )
			{
				Thread::sleep(1);
			}
		}

		//
		if( _state == Thread::THREAD_RUNNING )
		{
			BASTERD_LOG("joinin thread...");
			int rc = pthread_join (_thread,0);
			if( rc == 0 )
			{
				// DO NOTHING? Sure, the thread did finish ok.
				BASTERD_LOG("join was successfull");
				_state = Thread::THREAD_IDLE;
			}else
			{
				// NOTE: cannot change thread state because it is not safe.
				switch(rc)
				{
				case 3: // ESRCH = thread not found
					BASTERD_ERRF("Wait on thread <font color=red>" << _thread << "</font> failed: not found.");
					break;
				case 22: // EINVAL = thread is not joinable
					BASTERD_ERRF("Wait on thread <font color=red>" << _thread << "</font> failed: is not joinable.");
					break;
				case 35: // EDEADLK = deadlock detected
					BASTERD_ERRF("Wait on thread <font color=red>" << _thread << "</font> failed: deadlock detected or would occur when joining.");
					break;
				default:
					break;
				}
			}
		}
#else
		// error checking disabled
		pthread_join (_thread, NULL);
#endif
	}
	// --------------------------------------------------------------------
	void Thread::yield(void)
	{
		#if BASTERD_IS_WINDOWS
			Sleep(0);
		#else
			sched_yield();
		#endif
	}
	// --------------------------------------------------------------------
	bool Thread::isRunning(void) const
	{
			return _state == Thread::THREAD_RUNNING;
	}
	// --------------------------------------------------------------------
	Thread::ThreadState Thread::getState(void) const
	{
			return _state;
	}
	// --------------------------------------------------------------------
	void Thread::run(void)
	{
		// only runs if runnable is set
		if( _runnable )
		{
			_runnable->run();
		}
	}
	// --------------------------------------------------------------------
#if BASTERD_IS_WINDOWS
	UINT __stdcall Thread::_threadRunMethod(void* param)
	{
		Thread* thread = (Thread*)param;
			// runs the thread
			thread->run();
			thread->_state = Thread::THREAD_IDLE;
		ExitThread (0); // CRT exit

		// mscrt version
		//_endthreadex (0); // msvcrt version
		return 0;
	}
#else
	// --------------------------------------------------------------------
	void* Thread::_threadRunMethod(void* param)
	{
		// invokes the thread run method
		//Thread* thread = (Thread*)param;
		Thread* thread = reinterpret_cast<Thread*>(param);

		thread->_state = Thread::THREAD_RUNNING;
		// executes and regturns something
		thread->run();
		thread->_state = Thread::THREAD_IDLE;


		//pthread_exit (0);
		return 0;
	}
#endif
	// --------------------------------------------------------------------
}

#endif
